package tr.edu.yildiz.test;

import java.util.ArrayList;
import java.util.List;

import tr.edu.yildiz.markov.education.Educator;
import tr.edu.yildiz.markov.viterbi.ViterbiCalculator;
import tr.edu.yildiz.util.FileUtils;
import tr.edu.yildiz.util.LoggerUtils;
import tr.edu.yildiz.util.FileUtils.WordTypes;

/**
 * Educates, tests and finds success ratios of the system.
 * 
 * @author Onur Ulusu
 * 
 */
public class SystemTester {

	static int letterCountOfSystem;
	static int faultyWordCountInTestData;
	static int faultyLetterCountInTestData;
	static int correctedFaultyWordCount;
	static int correctedFaultyLetterCount;
	static int corruptedCorrectWordCount;
	static int corruptedCorrectLetterCount;
	static double successPercentageOfCorrectedWords;
	static double successPercentageOfCorrectedLetters;
	static double percentageOfCorruptedWords;
	static double percentageOfCorruptedLetters;
	private static List<String> faultyWords;
	private static List<String> correctWords;
	private static List<String> correctedWords = new ArrayList<String>();

	public static void main(String[] args) {

		// Initializing system logger.
		LoggerUtils.initializeLog4j();
		// Count faulty and correct words and letters of test input.
		analyzeTestData();

		Educator educator = new Educator();
		// Read input data and educate system with them.
		educator.educateFromFile();
		// Prepare system matrixes and initial state probabilites.
		educator.prepareInitialStateProbability();
		educator.prepareStateTransitionProbabilityMatrix();
		educator.prepareOutputProbabilityMatrix();

		// educator.getFaultyWordsFromFile();

		// Applying viterbi algorithm on test data.
		ViterbiCalculator viterbiCalc = new ViterbiCalculator();
		for (String currentFaultyWord : faultyWords) {
			String hiddenWord = viterbiCalc.viterbi(currentFaultyWord,
					educator.initialStateProbs,
					educator.stateTransitionProbMatrix,
					educator.outputProbabilityMatrix);
			correctedWords.add(hiddenWord);
		}

		// Counting corrected and corrupted letters and words.
		analyzeCorrectedData();
		// Calculating system's success..
		calculateSuccessRatios();

		LoggerUtils.logger.info("**************TEST RESULTS**************");
		LoggerUtils.logger
				.info("All letters in system: " + letterCountOfSystem);
		LoggerUtils.logger.info("faultyWordCountInTestData: "
				+ faultyWordCountInTestData);
		LoggerUtils.logger.info("faultyLetterCountInTestData: "
				+ faultyLetterCountInTestData);
		LoggerUtils.logger.info("correctedFaultyWordCount: "
				+ correctedFaultyWordCount);
		LoggerUtils.logger.info("correctedFaultyLetterCount: "
				+ correctedFaultyLetterCount);
		LoggerUtils.logger.info("corruptedCorrectWordCount: "
				+ corruptedCorrectWordCount);
		LoggerUtils.logger.info("corruptedCorrectLetterCount: "
				+ corruptedCorrectLetterCount);
		LoggerUtils.logger.info(String.format(
				"Percentage of corrected faulty words:  %2.02f",
				successPercentageOfCorrectedWords));
		LoggerUtils.logger.info(String.format(
				"Percentage of corrected faulty letters:  %2.02f",
				successPercentageOfCorrectedLetters));
		LoggerUtils.logger.info(String.format(
				"Percentage of corrupted words:  %2.02f",
				percentageOfCorruptedWords));
		LoggerUtils.logger.info(String.format(
				"Percentage of corrupted letters:  %2.02f",
				percentageOfCorruptedLetters));
		LoggerUtils.logger.info("**************TEST RESULTS**************");
	}

	private static void analyzeTestData() {
		faultyWords = FileUtils.retrieveTestWordsFromFile(WordTypes.FAULTY);
		correctWords = FileUtils.retrieveTestWordsFromFile(WordTypes.CORRECT);

		letterCountOfSystem = correctWords.size();

		for (int i = 0; i < correctWords.size(); i++) {
			String currentCorrectWord = correctWords.get(i);
			String currentFaultyWord = faultyWords.get(i);
			if (!currentCorrectWord.equals(currentFaultyWord)) {
				faultyWordCountInTestData++;
				for (int j = 0; j < currentCorrectWord.length(); j++) {
					if (currentCorrectWord.charAt(j) != currentFaultyWord
							.charAt(j)) {
						faultyLetterCountInTestData++;
					}
				}
			}
		}
	}

	private static void analyzeCorrectedData() {

		for (int i = 0; i < correctWords.size(); i++) {
			String currentFaulty = faultyWords.get(i);
			String currentCorrected = correctedWords.get(i);
			String currentCorrect = correctWords.get(i);

			if (currentFaulty.equals(currentCorrect)) {
				// Test word is already correct, checking if we corrupted it.
				if (!currentCorrected.equals(currentCorrect)) {
					// We corrupted the correct word :(
					corruptedCorrectWordCount++;
					for (int j = 0; j < currentCorrected.length(); j++) {
						if (currentCorrected.charAt(j) != currentCorrect
								.charAt(j)) {
							corruptedCorrectLetterCount++;
						}
					}
				}
			} else {
				// Test word is faulty, checking whether can we correct it.
				if (currentCorrected.equals(currentCorrect)) {
					// We corrected the faulty word :)) \o/
					correctedFaultyWordCount++;
					for (int j = 0; j < currentCorrected.length(); j++) {
						if (currentCorrected.charAt(j) != currentFaulty
								.charAt(j)) {
							correctedFaultyLetterCount++;
						}
					}
					LoggerUtils.logger.info("\nfaulty word:\t " + currentFaulty
							+ "\ncorrected word:\t " + currentCorrected
							+ "\noriginal word:\t " + currentCorrect);
				}
			}
		}
	}

	private static void calculateSuccessRatios() {

		successPercentageOfCorrectedWords = (((double) correctedFaultyWordCount) * 100)
				/ (double) faultyWordCountInTestData;
		successPercentageOfCorrectedLetters = (((double) correctedFaultyLetterCount) * 100)
				/ (double) faultyLetterCountInTestData;
		percentageOfCorruptedWords = (((double) corruptedCorrectWordCount) * 100)
				/ (double) letterCountOfSystem;
		percentageOfCorruptedLetters = (((double) corruptedCorrectLetterCount) * 100)
				/ (double) letterCountOfSystem;
	}
}
